#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PDF科技文献总结工具
功能：批量处理PDF科技文献，使用大模型进行内容总结，并输出到多种格式文件

版本: 2.0.0
作者: 陈振玺
"""

import os
import argparse
import json
import time
from pathlib import Path
from typing import List, Dict, Optional, Any
from .pdf_extractor import PDFExtractor
from .llm_summarizer import LLMSummarizer
from .report_generator import ReportGenerator
from .processed_files_manager import ProcessedFilesManager
from ..utils import ToolbeltLogger

# 延迟导入，避免在没有安装依赖时出错
try:
    from tqdm import tqdm
    TQDM_AVAILABLE = True
except ImportError:
    TQDM_AVAILABLE = False
    tqdm = None  # type: ignore


class PDFSummarizer:
    """
    PDF科技文献总结器
    
    功能：
    - 扫描指定目录下的所有PDF文件
    - 提取PDF文本内容（使用PyMuPDF提高效率）
    - 文本分块与清洗
    - 使用大模型对文献内容进行总结
    - 生成包含所有文献总结的多种格式报告
    - 显示每次总结的token消耗
    
    版本: 2.0.0
    作者: 陈振玺
    """
    
    def __init__(self, api_key: Optional[str] = None, model: str = "gpt-3.5-turbo", 
                 config_file: Optional[str] = None, max_retries: int = 3,
                 api_base: Optional[str] = None, print_output: bool = False,
                 chunk_size: int = 3000, interactive: bool = False):
        """
        初始化PDF总结器
        
        Args:
            api_key (str, optional): OpenAI API密钥（对于Ollama等本地服务可为空）
            model (str): 使用的大模型名称
            config_file (str, optional): 配置文件路径
            max_retries (int): 最大重试次数
            api_base (str, optional): API基础URL
            print_output (bool): 是否将模型输出打印到控制台
            chunk_size (int): 文本分块大小（字符数）
            interactive (bool): 是否启用交互模式
        """
        # 从配置文件读取配置
        config = self._load_config(config_file) if config_file else {}
        
        # 优先级：参数 > 配置文件 > 环境变量 > 默认值
        self.model = model or config.get("model") or os.getenv("OPENAI_MODEL") or "gpt-3.5-turbo"
        config_api_key = config.get("api_key")
        self.max_retries = max_retries or config.get("max_retries") or 3
        self.api_base = api_base or config.get("api_base") or os.getenv("OPENAI_API_BASE")
        self.print_output = print_output  # 添加打印输出控制属性
        self.chunk_size = chunk_size or config.get("chunk_size") or 3000  # 文本分块大小
        self.interactive = interactive  # 是否启用交互模式
        
        # 对于Ollama等本地服务，如果没有提供API密钥，则使用默认值或空字符串
        final_api_key = api_key or config_api_key or os.getenv("OPENAI_API_KEY") or "ollama"
        
        # 初始化各组件
        self.pdf_extractor = PDFExtractor()
        self.llm_summarizer = LLMSummarizer(
            api_key=final_api_key,
            model=self.model,
            api_base=self.api_base,
            max_retries=self.max_retries,
            print_output=self.print_output
        )
        self.report_generator = ReportGenerator()
        
        # 如果没有API密钥或依赖不可用，将使用模拟模式
        self.simulation_mode = self.llm_summarizer.simulation_mode
    
    def _load_config(self, config_file: Optional[str]) -> Dict[str, Any]:
        """
        从配置文件加载配置
        
        Args:
            config_file (str, optional): 配置文件路径
            
        Returns:
            Dict[str, Any]: 配置字典
        """
        if not config_file:
            # 尝试默认配置文件位置
            default_config_paths = [
                Path.home() / ".pdf_summarizer_config.json",
                Path(".") / "pdf_summarizer_config.json",
                Path(".") / ".config" / "pdf_summarizer.json"
            ]
            
            for path in default_config_paths:
                if path.exists():
                    config_file = str(path)
                    break
            else:
                return {}
        
        config_path = Path(config_file)
        if not config_path.exists():
            ToolbeltLogger.warning(f"配置文件不存在: {config_file}")
            return {}
        
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            ToolbeltLogger.warning(f"无法读取配置文件 {config_file}: {e}")
            return {}
    
    def scan_pdf_files(self, directory: Path) -> List[Path]:
        """
        扫描目录下的所有PDF文件，跳过以点开头的文件
        
        Args:
            directory (Path): 要扫描的目录
            
        Returns:
            List[Path]: PDF文件路径列表
        """
        pdf_files = []
        for file_path in directory.rglob("*.pdf"):
            # 跳过以点开头的文件（隐藏文件）
            if file_path.name.startswith('.'):
                continue
            if file_path.is_file():
                pdf_files.append(file_path)
        return pdf_files
    
    def _get_user_feedback(self, filename: str, summary: str) -> tuple[bool, str]:
        """
        获取用户对总结结果的反馈
        
        Args:
            filename (str): 文件名
            summary (str): 总结内容
            
        Returns:
            tuple[bool, str]: (是否接受, 反馈原因)
        """
        print(f"\n=== {filename} 的总结结果 ===")
        print(summary)
        print("=" * 50)
        
        while True:
            user_input = input("\n您对这个总结结果满意吗？(y/n): ").strip().lower()
            if user_input in ['y', 'yes', '是', '满意']:
                return True, ""
            elif user_input in ['n', 'no', '否', '不满意']:
                reason = input("请输入不满意的原因: ").strip()
                return False, reason
            else:
                print("请输入 y(是) 或 n(否)")
    
    def _reprocess_with_feedback(self, text: str, filename: str, feedback: str) -> tuple[str, Optional[Dict[str, int]]]:
        """
        根据用户反馈重新处理文本
        
        Args:
            text (str): 原始文本
            filename (str): 文件名
            feedback (str): 用户反馈
            
        Returns:
            tuple[str, Optional[Dict[str, int]]]: 总结内容和token消耗信息
        """
        ToolbeltLogger.info(f"根据用户反馈重新处理 {filename}")
        
        # 构造包含反馈的提示词
        prompt = f"""
请重新总结以下文献内容。

用户反馈的问题: {feedback}

请特别注意解决上述问题，并按照之前的格式要求生成总结。

文献原文:
{text}
        """
        
        # 使用LLM重新总结
        return self.llm_summarizer.summarize_text(text, filename)
    
    def process_pdfs(self, input_directory: Path) -> List[Dict[str, Any]]:
        """
        处理目录中的所有PDF文件
        
        Args:
            input_directory (Path): 包含PDF文件的目录
            
        Returns:
            List[Dict[str, Any]]: 包含文件名和总结的字典列表
        """
        ToolbeltLogger.info(f"开始处理目录中的PDF文件: {input_directory}")
        
        # 初始化已处理文件管理器
        processed_files_path = input_directory / ".processed_files.json"
        processed_files_manager = ProcessedFilesManager(processed_files_path)
        
        # 扫描PDF文件
        pdf_files = self.scan_pdf_files(input_directory)
        ToolbeltLogger.info(f"找到 {len(pdf_files)} 个PDF文件")
        
        if not pdf_files:
            ToolbeltLogger.info("未找到PDF文件")
            return []
        
        summaries = []
        
        # 使用tqdm显示进度
        pdf_files_iter = tqdm(pdf_files, desc="处理PDF文件", unit="个") if TQDM_AVAILABLE and tqdm is not None else pdf_files
        
        # 处理每个PDF文件
        for pdf_path in pdf_files_iter:
            filename = pdf_path.name
            
            # 检查文件是否已处理
            if processed_files_manager.is_file_processed(filename):
                ToolbeltLogger.info(f"跳过已处理文件: {filename}")
                # 从已处理文件中获取之前的结果
                processed_data = processed_files_manager.processed_files[filename]
                summaries.append({
                    "filename": filename,
                    "filepath": processed_data["filepath"],
                    "summary": processed_data["summary"],
                    "success": True,
                    "skipped": True  # 标记为跳过的文件
                })
                continue
            
            if TQDM_AVAILABLE and tqdm is not None and isinstance(pdf_files_iter, tqdm):
                pdf_files_iter.set_postfix(文件=filename)
            else:
                ToolbeltLogger.info(f"处理文件: {filename}")
            
            # 提取文本（使用智能提取方法）
            text = self.pdf_extractor.extract_main_text(pdf_path)
            if not text.strip():
                ToolbeltLogger.info(f"  跳过 {filename} (无法提取文本)")
                summaries.append({
                    "filename": filename,
                    "filepath": str(pdf_path.relative_to(input_directory)),
                    "summary": f"❌ 无法提取 {filename} 的文本内容，跳过处理。",
                    "success": False
                })
                continue
            
            # 不再分块，直接使用全部文本
            ToolbeltLogger.info(f"  文本长度: {len(text)} 字符")
            
            # 生成总结（使用全部文本）
            summary, token_usage = self.llm_summarizer.summarize_text(text, filename)
            
            # 如果启用交互模式，获取用户反馈
            if self.interactive:
                accepted = False
                retry_count = 0
                max_retries = 3
                
                while not accepted and retry_count < max_retries:
                    accepted, feedback = self._get_user_feedback(filename, summary)
                    
                    if not accepted:
                        retry_count += 1
                        if retry_count < max_retries:
                            ToolbeltLogger.info(f"根据用户反馈重新处理 {filename} (第{retry_count}次)")
                            summary, token_usage = self._reprocess_with_feedback(text, filename, feedback)
                        else:
                            ToolbeltLogger.warning(f"已达到最大重试次数，保留当前总结结果")
                            # 即使用户不满意，也保留最后一次结果
                            break
                    else:
                        ToolbeltLogger.info(f"用户接受 {filename} 的总结结果")
            
            # 判断是否成功
            success = not summary.startswith("无法生成") and not summary.startswith("❌")
            
            # 保存结果
            result = {
                "filename": filename,
                "filepath": str(pdf_path.relative_to(input_directory)),
                "summary": summary,
                "success": success
            }
            
            # 如果有token消耗信息，也保存
            if token_usage:
                result["token_usage"] = token_usage
            
            summaries.append(result)
            
            # 如果处理成功且启用了跳过功能，将文件添加到已处理列表
            if success:
                processed_files_manager.add_processed_file(filename, {
                    "filepath": str(pdf_path.relative_to(input_directory)),
                    "processed_time": time.strftime('%Y-%m-%d %H:%M:%S'),
                    "summary": summary
                })
        
        return summaries


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="PDF科技文献总结工具")
    parser.add_argument(
        'input_directory',
        type=Path,
        help='包含PDF文件的目录'
    )
    
    parser.add_argument(
        '--api-key',
        type=str,
        help='OpenAI API密钥（对于Ollama等本地服务可为空）'
    )
    
    parser.add_argument(
        '--model',
        type=str,
        default="gpt-3.5-turbo",
        help='使用的大模型名称 (默认: gpt-3.5-turbo)'
    )
    
    parser.add_argument(
        '--config',
        type=str,
        help='配置文件路径'
    )
    
    parser.add_argument(
        '--max-retries',
        type=int,
        default=3,
        help='最大重试次数 (默认: 3)'
    )
    
    parser.add_argument(
        '--api-base',
        type=str,
        help='API基础URL'
    )
    
    parser.add_argument(
        '--print-output',
        action='store_true',
        help='将模型输出打印到控制台'
    )
    
    parser.add_argument(
        '--chunk-size',
        type=int,
        default=3000,
        help='文本分块大小（字符数）(默认: 3000) [已弃用]'
    )
    
    parser.add_argument(
        '--output-format',
        choices=['md', 'json', 'txt', 'all'],
        default='md',
        help='输出格式: md (Markdown), json (JSON), txt (文本), all (所有格式) (默认: md)'
    )
    
    parser.add_argument(
        '--interactive',
        action='store_true',
        help='启用交互模式，每个PDF处理完后等待用户确认'
    )
    
    args = parser.parse_args()
    
    try:
        # 检查依赖
        dependencies = []
        if not TQDM_AVAILABLE:
            dependencies.append("tqdm")
        
        try:
            import fitz  # PyMuPDF
        except ImportError:
            dependencies.append("PyMuPDF")
        
        try:
            from openai import OpenAI
        except ImportError:
            dependencies.append("openai")
        
        if dependencies:
            ToolbeltLogger.warning(f"未安装必要的依赖 ({', '.join(dependencies)})")
            print("请运行: pip install " + " ".join(dependencies))
        
        # 创建总结器实例
        summarizer = PDFSummarizer(
            api_key=args.api_key, 
            model=args.model,
            config_file=args.config,
            max_retries=args.max_retries,
            api_base=args.api_base,
            print_output=args.print_output,
            interactive=args.interactive
            # chunk_size 参数已移除
        )
        
        # 处理PDF文件
        summaries = summarizer.process_pdfs(args.input_directory)
        
        # 生成报告
        report_generator = ReportGenerator()
        report_generator.generate_report(summaries, output_format=args.output_format)
        
    except Exception as e:
        ToolbeltLogger.error(f"发生错误: {e}")
